import { Component, OnDestroy, OnInit, HostListener } from '@angular/core';
import { Router } from '@angular/router';
import { MatDialog } from '@angular/material';
import { RestService } from '../services/rest.service';
import { TranslateService } from '@ngx-translate/core';
import { BedDialogComponent } from './bed-dialog/bed-dialog.component';
import { DialogTypes, ActionTypes, ObjectTypes } from '../models/wdl-types';

import * as _ from 'lodash';

@Component({
  templateUrl: './bed.component.html',
  styleUrls: ['./bed.component.css'],
})
export class BedComponent implements OnInit, OnDestroy {
  institution: any;
  user: any;
  building: any;
  level: any;
  room: any;

  buildings = [];
  levels = [];
  rooms = [];
  beds = [];

  colNumber = 1;

  type: ObjectTypes;

  @HostListener('window:resize', ['$event'])
  onResize(event) {
    this.calculateColNumber();
  }

  constructor(public dialog: MatDialog,
              private router: Router,
              private translate: TranslateService,
              private rest: RestService) {
    this.type = ObjectTypes.BUILDING;
    this.institution = this.rest.getInstitution();
    this.user = this.rest.getUser();
    this.calculateColNumber();

    if (!this.institution.id) {
      console.error('Instituion is invalid: ' + this.institution.id);
    }
  }

  isUserQualified() {
    return this.user.role === 'header';
  }

  calculateColNumber() {
    const newNumber = (window.innerWidth - 200) / 290;
    this.colNumber = newNumber < 1 ? 1 : newNumber;
  }

  getType(): string {
    switch (this.type) {
    case ObjectTypes.BUILDING:
      return 'building';
    case ObjectTypes.LEVEL:
      return 'level';
    case ObjectTypes.ROOM:
      return 'room';
    case ObjectTypes.BED:
      return 'bed';
    }
  }

  isBuilding() {
    return this.type === ObjectTypes.BUILDING;
  }

  isLevel() {
    return this.type === ObjectTypes.LEVEL;
  }

  geLevel() {
    return this.type >= ObjectTypes.LEVEL;
  }

  isRoom() {
    return this.type === ObjectTypes.ROOM;
  }

  geRoom() {
    return this.type >= ObjectTypes.ROOM;
  }

  isBed() {
    return this.type === ObjectTypes.BED;
  }

  geBed() {
    return this.type >= ObjectTypes.BED;
  }

  getUrl() {
    return 'data/' + this.getType();
  }

  getListUrl() {
    switch (this.type) {
    case ObjectTypes.BUILDING:
      return 'data/institution/' + this.institution.id + '/buildings';
    case ObjectTypes.LEVEL:
      return 'data/building/' + this.building.id + '/levels';
    case ObjectTypes.ROOM:
      return 'data/level/' + this.level.id + '/rooms';
    case ObjectTypes.BED:
      return 'data/room/' + this.room.id + '/beds';
    }
  }

  getModifyUrl(item) {
    switch (this.type) {
    case ObjectTypes.BUILDING:
      return 'data/building/' + item.id;
    case ObjectTypes.LEVEL:
      return 'data/level/' + item.id;
    case ObjectTypes.ROOM:
      return 'data/room/' + item.id;
    case ObjectTypes.BED:
      return 'data/bed/' + item.id;
    }
  }

  getLinkUrl(data) {
    const linkUrls = [];
    switch (this.type) {
    case ObjectTypes.BUILDING:
      linkUrls.push(this.getUrl() + '/' + data['id'] + '/institution');
      linkUrls.push('data/institution/' + this.institution.id);
      break;
    case ObjectTypes.LEVEL:
      linkUrls.push(this.getUrl() + '/' + data['id'] + '/building');
      linkUrls.push('data/building/' + this.building.id);
      break;
    case ObjectTypes.ROOM:
      linkUrls.push(this.getUrl() + '/' + data['id'] + '/level');
      linkUrls.push('data/level/' + this.level.id);
      break;
    case ObjectTypes.BED:
      linkUrls.push(this.getUrl() + '/' + data['id'] + '/room');
      linkUrls.push('data/room/' + this.room.id);
      break;
    }

    return linkUrls;
  }

  isConflict(data) {
    let index = -1;
    switch (this.type) {
    case ObjectTypes.BUILDING:
      index = _.findIndex(this.buildings, {buildingName: data['buildingName']});
      break;
    case ObjectTypes.LEVEL:
      index = _.findIndex(this.levels, {levelName: data['levelName']});
      break;
    case ObjectTypes.ROOM:
      index = _.findIndex(this.rooms, {roomName: data['roomName']});
      break;
    case ObjectTypes.BED:
      index = _.findIndex(this.beds, {bedName: data['bedName']});
      break;
    }

    return index > -1;
  }

  add() {
    this.dialog.open(BedDialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '450px',
      data: {
        mode: ActionTypes.ADD,
        type: this.type
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      // check duplication
      if (this.isConflict(data)) {
        const title = this.translate.get('objectExistsTitle')['value'];
        const message = this.translate.get('objectExistsMessage')['value'];
        this.rest.openGenDialog(DialogTypes.INFO, title, message, null);
        return;
      }

      const nameString = this.getNameString();
      if (nameString) {
        data['nameString'] = nameString + '##' + data.bedName;
      }

      const idString = this.getIdString();
      if (idString) {
        data['idString'] = idString;
      }

      this.rest.post(this.getUrl(), data).subscribe(response => {
        const linkUrls = this.getLinkUrl(response);
        this.rest.link(linkUrls[0], linkUrls[1]).subscribe(() => {
          console.log('Linkage was done: ' + linkUrls.join(','));
          this.refresh();
        }, () => {
          console.error('Failed to link: ' + linkUrls.join(','));
          this.refresh();
        });
      }, message => {
        this.rest.showHttpError('addBuildingError', message);
      });
    });
  }

  getNameString() {
    return this.type === ObjectTypes.BED  ? (this.building.buildingName + '##' + this.level.levelName + '##' + this.room.roomName) : '';
  }

  getIdString() {
    return this.type === ObjectTypes.BED  ? (this.building.id + '##' + this.level.id + '##' + this.room.id) : '';
  }

  modify(event) {
    this.dialog.open(BedDialogComponent, {
      disableClose: false,
      autoFocus: false,
      width: '450px',
      data: {
        mode: ActionTypes.MODIFY,
        item: event.item,
        type: event.type
      }
    }).afterClosed().subscribe(data => {
      if (!data || _.isString(data)) {
        return;
      }

      // check duplication
      if (this.isConflict(data)) {
        const title = this.translate.get('objectExistsTitle')['value'];
        const message = this.translate.get('objectExistsMessage')['value'];
        this.rest.openGenDialog(DialogTypes.INFO, title, message, null);
        return;
      }

      const nameString = this.getNameString();
      if (nameString) {
        data['nameString'] = nameString;
      }

      this.rest.patch(this.getModifyUrl(event.item), data).subscribe(() => {
        data['id'] = event.item.id;
        this.updateNameString(event.type, data);
        this.refresh();
      }, message => {
        this.rest.showHttpError('modifyBuildingError', message);
      });
    });
  }

  updateNameString(type, item) {
    switch (type) {
    case ObjectTypes.BUILDING:
      this.updateBuildingNameString(item);
      break;
    case ObjectTypes.LEVEL:
      this.updateLevelNameString(this.building.buildingName + '##' + item.levelName, item);
      break;
    case ObjectTypes.ROOM:
      this.updateRoomNameString(this.building.buildingName + '##' + this.level.levelName + '##' + item.roomName, item);
      break;
    case ObjectTypes.BED:
      this.updateBedNameString(this.building.buildingName + '##' + this.level.levelName + '##' + this.room.roomName, item);
      break;
    }
  }

  updateBuildingNameString(building) {
    this.rest.get('data/building/' + building.id + '/levels').subscribe(data => {
      data['_embedded']['levels'].forEach(level => {
        this.updateLevelNameString(building.buildingName + '##' + level.levelName, level);
      });
    });
  }

  updateLevelNameString(nameString, level) {
    this.rest.get('data/level/' + level.id + '/rooms').subscribe(data => {
      data['_embedded']['rooms'].forEach(room => {
        this.updateRoomNameString(nameString + '##' + room.roomName, room);
      });
    });
  }

  updateRoomNameString(nameString, room) {
    this.rest.get('data/room/' + room.id + '/beds').subscribe(data => {
      data['_embedded']['beds'].forEach(bed => {
        this.updateBedNameString(nameString, bed);
      });
    });
  }

  updateBedNameString(nameString, bed) {
    bed['nameString'] = nameString + '##' + bed.bedName;
    this.rest.patch('data/bed/' + bed.id, bed).subscribe(() => {
      console.log('name string was updated for bed: ' + bed.id + ', ' + nameString);
      // Try to update the bedString in person table
      this.rest.getWithParams('data/person/search/bed', {
        institutionId: this.institution.id,
        bedId: bed.id
      }).subscribe(resp => {
        resp['_embedded']['persons'].forEach(person => {
          person.bedString = bed.nameString;
          this.rest.patch('data/person/' + person.id, {
            bedString: bed.nameString
          }).subscribe(() => {
            console.log('bedString was updated for person ' + person.personName);
          });
        });
      });
    });
  }

  refresh() {
    switch (this.type) {
    case ObjectTypes.BUILDING:
      this.rest.get('data/institution/' + this.institution.id + '/buildings').subscribe(data => {
        this.buildings = data['_embedded']['buildings'];
      });
      break;
    case ObjectTypes.LEVEL:
      this.rest.get('data/building/' + this.building.id + '/levels').subscribe(data => {
        this.levels = data['_embedded']['levels'];
      });
      break;
    case ObjectTypes.ROOM:
      this.rest.get('data/level/' + this.level.id + '/rooms').subscribe(data => {
        this.rooms = data['_embedded']['rooms'];
      });
      break;
    case ObjectTypes.BED:
      this.rest.get('data/room/' + this.room.id + '/beds').subscribe(data => {
        this.beds = data['_embedded']['beds'];
      });
      break;
    }
  }

  goto(event) {
    switch (event.type) {
    case ObjectTypes.BUILDING:
      this.building = event.item;
      this.type = ObjectTypes.LEVEL;
      break;
    case ObjectTypes.LEVEL:
      this.level = event.item;
      this.type = ObjectTypes.ROOM;
      break;
    case ObjectTypes.ROOM:
      this.room = event.item;
      this.type = ObjectTypes.BED;
      break;
    }

    this.refresh();
  }

  backTo(type) {
    switch (type) {
    case 'building':
      this.type = ObjectTypes.BUILDING;
      break;
    case 'level':
      this.type = ObjectTypes.LEVEL;
      break;
    case 'room':
      this.type = ObjectTypes.ROOM;
      break;
    }

    this.refresh();
  }

  ngOnInit() {
    this.refresh();
  }

  ngOnDestroy() {
  }
}
